/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools resize load super block
 */

#include "load_super_block.h"

#include <clocale>
#include <cstdbool>
#include <ctime>

#include "device_manager.h"
#include "hmfs_common.h"
#include "node.h"
#include "resize_load.h"

namespace OHOS {
namespace Hmfs {
LoadSuperBlock::LoadSuperBlock(ResizeLoad *loadPtr) : load_(loadPtr)
{
    config_ = load_->GetConfig();
    superBlockInfo_ = load_->GetSuperBlockInfo();
}

int32_t LoadSuperBlock::SetFeature(int32_t feature)
{
    if (config_->feature & NativeToLE32(feature)) {
        if (!(superBlockInfo_->rawSuper->features & NativeToLE32(feature))) {
            superBlockInfo_->rawSuper->features |= NativeToLE32(feature);
            return EXIT_ERR_CODE;
        }
    }
    return SUCCESS_CODE;
}

void LoadSuperBlock::SetFeatures(SuperBlockAddr superBlockAddr)
{
    bool requiresUpdate = false;
    if (SetFeature(HMFS_FEATURE_EXTRA_ATTR)) {
        HMFS_INFO("Fix set feature: extra_attr");
        requiresUpdate = true;
    }

    if (SetFeature(HMFS_FEATURE_PRJQUOTA)) {
        HMFS_INFO("Fix set feature: project_quota");
        requiresUpdate = true;
    }

    if (SetFeature(HMFS_FEATURE_CASEFOLD)) {
        SuperBlockData *superBlock = superBlockInfo_->rawSuper;
        SetLeValue(superBlock->encoding, config_->fileEncoding);
        SetLeValue(superBlock->encodingFlags, config_->fileEncodingFlags);
        HMFS_INFO("Fix set feature: casefold, encoding: %d, encodingFlags: %d",
            config_->fileEncoding, config_->fileEncodingFlags);
        requiresUpdate = true;
    }

    if (requiresUpdate) {
        UpdateSuperBlock(superBlockInfo_->rawSuper, SUPER_BLOCK_MASK(superBlockAddr));
    }
}

int32_t LoadSuperBlock::CheckAddrBoundary(SuperBlockData *superBlock, SuperBlockAddr superBlockAddr)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if (CheckSegment0AndCheckpoint(superBlock) != SUCCESS_CODE) {
        return EXIT_ERR_CODE;
    }

    if (CheckBoundary(superBlock, superBlock->checkPointBlockId, superBlock->sitBlockId,
        superBlock->segmentCountInCP, "CP") != SUCCESS_CODE) {
        return EXIT_ERR_CODE;
    }

    if (CheckBoundary(superBlock, superBlock->sitBlockId, superBlock->natBlockId,
        superBlock->segmentCountInSIT, "SIT") != SUCCESS_CODE) {
        return EXIT_ERR_CODE;
    }

    if (CheckBoundary(superBlock, superBlock->natBlockId, superBlock->ssaBlockId,
        superBlock->segmentCountInNAT, "NAT") != SUCCESS_CODE) {
        return EXIT_ERR_CODE;
    }

    if (CheckBoundary(superBlock, superBlock->ssaBlockId, superBlock->mainBlockId,
        superBlock->segmentCountInSSA, "SSA") != SUCCESS_CODE) {
        return EXIT_ERR_CODE;
    }

    return CheckMainArea(superBlock, superBlockAddr);
}

int32_t LoadSuperBlock::CheckSegment0AndCheckpoint(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    uint32_t segment0BlockId = GetLeValue(superBlock->segment0BlockId);
    uint32_t checkPointBlockId = GetLeValue(superBlock->checkPointBlockId);
    if (segment0BlockId != checkPointBlockId) {
        HMFS_ERROR("Mismatch segment0(%u) cp_blkaddr(%u)", segment0BlockId, checkPointBlockId);
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckBoundary(SuperBlockData *superBlock, uint32_t startBlockId, uint32_t endBlockId,
    uint32_t segmentCount, const char *boundaryName)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    uint32_t logBlksPerSeg = GetLeValue(superBlock->logBlksPerSeg);
    if (startBlockId + (segmentCount << logBlksPerSeg) != endBlockId) {
        HMFS_ERROR("Wrong %s boundary, start(%u) end(%u) blocks(%u)",
            boundaryName, startBlockId, endBlockId, segmentCount << logBlksPerSeg);
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckMainArea(SuperBlockData *superBlock, SuperBlockAddr superBlockAddr)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    uint32_t mainBlockId = GetLeValue(superBlock->mainBlockId);
    uint32_t segment0BlockId = GetLeValue(superBlock->segment0BlockId);
    uint32_t segmentCount = GetLeValue(superBlock->segmentCount);
    uint32_t segmentCountInMain = GetLeValue(superBlock->segmentCountInMain);
    uint32_t logBlksPerSeg = GetLeValue(superBlock->logBlksPerSeg);
    uint64_t mainEndBlockAddr = mainBlockId + (segmentCountInMain << logBlksPerSeg);
    uint64_t segEndBlockAddr = segment0BlockId + (segmentCount << logBlksPerSeg);

    if (mainEndBlockAddr > segEndBlockAddr) {
        HMFS_ERROR("Wrong MAIN_AREA, start(%u) end(%" PRIu64 ") block(%u)",
            mainBlockId, segEndBlockAddr, segmentCountInMain << logBlksPerSeg);
        return EXIT_ERR_CODE;
    } else if (mainEndBlockAddr < segEndBlockAddr) {
        SetLeValue(superBlock->segmentCount, (mainEndBlockAddr - segment0BlockId) >> logBlksPerSeg);
        UpdateSuperBlock(superBlock, 1 << static_cast<int32_t>(superBlockAddr));
        HMFS_ERROR("Fix alignment: start(%u) end(%" PRIu64 ") block(%u)", mainBlockId,
            segEndBlockAddr, segmentCountInMain << logBlksPerSeg);
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::VerifySbChecksum(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if (SB_CHECKSUM_OFFSET != GetLeValue(superBlock->checksumOffset)) {
        HMFS_ERROR("Invalid SB CRC offset: %u", GetLeValue(superBlock->checksumOffset));
        return EXIT_ERR_CODE;
    }
    if (HmfsCommon::GetInstance().CheckCrcValid(GetLeValue(superBlock->checksum),
        superBlock, GetLeValue(superBlock->checksumOffset))) {
        HMFS_ERROR("Invalid SB CRC: 0x%x", GetLeValue(superBlock->checksum));
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckSuperBlock(SuperBlockData *superBlock, SuperBlockAddr superBlockAddr)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckMagicNumber(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckFeaturesAndChecksum(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckBlockSize(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckSegmentCounts(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckDeviceSegments(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckExtensions(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckInodeIds(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckZonedModel(superBlock) == SUCCESS_CODE, return EXIT_ERR_CODE);
    HMFS_CHECK_ONLY_EXPER(CheckAddrBoundary(superBlock, superBlockAddr) == SUCCESS_CODE, return EXIT_ERR_CODE);
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckMagicNumber(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if (HMFS_MAGIC_NUMBER != GetLeValue(superBlock->magicNumber)) {
        HMFS_ERROR("Magic Mismatch, valid(0x%x) - read(0x%x)", HMFS_MAGIC_NUMBER,
            GetLeValue(superBlock->magicNumber));
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckFeaturesAndChecksum(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if ((GetLeValue(superBlock->features) & HMFS_FEATURE_SB_CHKSUM) && VerifySbChecksum(superBlock)) {
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckBlockSize(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    uint32_t blockSize = 1 << GetLeValue(superBlock->logBlockSize);
    if (HMFS_BLOCK_SIZE != blockSize) {
        HMFS_ERROR("Invalid blocksize (%u), supports only 4KB", blockSize);
        return EXIT_ERR_CODE;
    }
    constexpr int32_t logBlocksPerSegment = 9;
    if (GetLeValue(superBlock->logBlksPerSeg) != logBlocksPerSegment) {
        HMFS_ERROR("Invalid log blocks per segment (%u)", GetLeValue(superBlock->logBlksPerSeg));
        return EXIT_ERR_CODE;
    }

    uint32_t logSectorSize = GetLeValue(superBlock->logSectorSize);
    if (logSectorSize > HMFS_MAX_LOG_SECTOR_SIZE || logSectorSize < HMFS_MIN_LOG_SECTOR_SIZE) {
        HMFS_ERROR("Invalid log sectorsize (%u)", logSectorSize);
        return EXIT_ERR_CODE;
    }

    if (GetLeValue(superBlock->log2SectorsPerBlock) + logSectorSize != HMFS_MAX_LOG_SECTOR_SIZE) {
        HMFS_ERROR("Invalid log sectors per block(%u) log sectorsize(%u)",
            GetLeValue(superBlock->log2SectorsPerBlock), logSectorSize);
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckSegmentCounts(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    uint32_t segmentCount = GetLeValue(superBlock->segmentCount);
    uint32_t segsPerSec = GetLeValue(superBlock->segsPerSection);
    uint32_t totalSections = GetLeValue(superBlock->sectionCount);
    if (segmentCount > HMFS_MAX_SEGMENT || segmentCount < HMFS_MIN_SEGMENT_COUNT) {
        HMFS_ERROR("Invalid segment count (%u)", segmentCount);
        return EXIT_ERR_CODE;
    }

    if (!(GetLeValue(superBlock->features) & NativeToLE32(HMFS_FEATURE_RO)) &&
        (totalSections > segmentCount || totalSections < HMFS_MIN_SEGMENT_COUNT ||
        segsPerSec > segmentCount || !segsPerSec)) {
        HMFS_ERROR("Invalid segment/section count (%u, %u x %u)", segmentCount, totalSections, segsPerSec);
        return EXIT_ERR_CODE;
    }

    if (segsPerSec != 0 && (segmentCount / segsPerSec) < totalSections) {
        HMFS_ERROR("Small segmentCount (%u < %u * %u)", segmentCount, segsPerSec, totalSections);
        return EXIT_ERR_CODE;
    }

    if (segmentCount > (GetLeValue(superBlock->blockCount) / BLOCKS_PER_SEGMENT)) {
        HMFS_ERROR("Wrong segmentCount / block_count (%u > %" PRIu64 ")",
            segmentCount, GetLeValue(superBlock->blockCount));
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckDeviceSegments(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if (superBlock->devices[0].path[0]) {
        uint32_t segmentCounts = LE32ToNative(superBlock->devices[0].segmentCount);
        for (uint32_t i = 1; i < MAX_DEVICE_COUNT && superBlock->devices[i].path[0]; ++i) {
            segmentCounts += LE32ToNative(superBlock->devices[i].segmentCount);
        }
        if (GetLeValue(superBlock->segmentCount) != segmentCounts / GetLeValue(superBlock->sectionsPerZone) *
            GetLeValue(superBlock->sectionsPerZone)) {
            HMFS_ERROR("Segment count (%u) mismatch with total segments from devices (%u)",
                GetLeValue(superBlock->segmentCount), segmentCounts);
            return EXIT_ERR_CODE;
        }
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckExtensions(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if (GetLeValue(superBlock->coldExtensionCount) > EXTENSION_COUNT_MAX ||
        superBlock->hotExtensionCount > EXTENSION_COUNT_MAX ||
        GetLeValue(superBlock->coldExtensionCount) + superBlock->hotExtensionCount > EXTENSION_COUNT_MAX) {
        HMFS_ERROR("Invalid extension count (%u + %u > %u)",
            GetLeValue(superBlock->coldExtensionCount), superBlock->hotExtensionCount, EXTENSION_COUNT_MAX);
        return EXIT_ERR_CODE;
    }
    uint32_t blocksPerSeg = 1 << GetLeValue(superBlock->logBlksPerSeg);
    if (GetLeValue(superBlock->checkPointPayload) > (blocksPerSeg - HMFS_CP_COUNT)) {
        HMFS_ERROR("Invalid checkPointPayload (%u > %u)",
            GetLeValue(superBlock->checkPointPayload), blocksPerSeg - HMFS_CP_COUNT);
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckInodeIds(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    constexpr int32_t metaInodeId = 2;
    constexpr int32_t rootInodeId = 3;
    if (GetLeValue(superBlock->nodeInodeId) != 1 || GetLeValue(superBlock->metaInodeId) != metaInodeId ||
        GetLeValue(superBlock->rootInodeId) != rootInodeId) {
        HMFS_ERROR("Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)",
            GetLeValue(superBlock->nodeInodeId), GetLeValue(superBlock->metaInodeId),
            GetLeValue(superBlock->rootInodeId));
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::CheckZonedModel(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    if (config_->devices[0].zonedModel != HMFS_ZONED_NONE && !(superBlock->features &
        NativeToLE32(HMFS_FEATURE_BLKZONED))) {
        HMFS_ERROR("Missing zoned block device feature");
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

bool LoadSuperBlock::isCheckpointStop(SuperBlockData *superBlock, bool abnormal)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return false);
    for (uint32_t i = 0; i < CP_STOP_REASON_MAX; i++) {
        if (abnormal && i == CP_STOP_REASON_SHUTDOWN) {
            continue;
        }
        if (superBlock->stopReason[i]) {
            return true;
        }
    }
    return false;
}

bool LoadSuperBlock::isInconsistentError(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return false);
    for (int32_t i = 0; i < HMFS_MAX_ERRORS; i++) {
        if (superBlock->errors[i]) {
            return true;
        }
    }
    return false;
}

int32_t LoadSuperBlock::ValidateSuperBlock(SuperBlockAddr superBlockAddr)
{
    HMFS_DEBUG("Enter ValidateSuperBlock");
    char buf[HMFS_BLOCK_SIZE];
    superBlockInfo_->rawSuper = static_cast<SuperBlockData *>(malloc(sizeof(SuperBlockData)));
    if (!superBlockInfo_->rawSuper) {
        return EXIT_ERR_CODE;
    }
    if (HmfsIo::GetInstance().DevReadBlock(buf, static_cast<int32_t>(superBlockAddr))) {
        HMFS_ERROR("failed to read superblock!");
        return EXIT_ERR_CODE;
    }
    HMFS_CHECK_ONLY_EXPER(memcpy_s(superBlockInfo_->rawSuper, sizeof(SuperBlockData),
        buf + HMFS_SUPER_BLOCK_OFFSET, sizeof(SuperBlockData)) == 0, return EXIT_ERR_CODE);
    if (CheckSuperBlock(superBlockInfo_->rawSuper, superBlockAddr) == SUCCESS_CODE) {
        SetFeatures(superBlockAddr);
        HmfsCommon::GetInstance().ReadKernelVersion(config_->version, sizeof(config_->version));
        HMFS_CHECK_ONLY_EXPER(memcpy_s(config_->sbVersion, VERSION_STRING_LEN,
            superBlockInfo_->rawSuper->version, VERSION_STRING_LEN) == 0, return EXIT_ERR_CODE);
        HmfsCommon::GetInstance().GetKernelVersion(config_->sbVersion);
        HMFS_CHECK_ONLY_EXPER(memcpy_s(config_->initVersion, VERSION_STRING_LEN,
            superBlockInfo_->rawSuper->initVersion, VERSION_STRING_LEN) == 0, return EXIT_ERR_CODE);
        HmfsCommon::GetInstance().GetKernelVersion(config_->initVersion);
        config_->forceStop = isCheckpointStop(superBlockInfo_->rawSuper, false);
        config_->abnormalStop = isCheckpointStop(superBlockInfo_->rawSuper, true);
        config_->hmfsErrors = isInconsistentError(superBlockInfo_->rawSuper);

        HMFS_INFO("RESIZE version\n  \"%s\"", config_->initVersion);
        HMFS_INFO("FSCK version\n  from \"%s\"\n    to \"%s\"", config_->sbVersion, config_->version);
        PrintSuperBlockDataFeature(superBlockInfo_->rawSuper);
        PrintSuperBlockDataStopReason(superBlockInfo_->rawSuper, config_->forceStop);
        PrintSuperBlockDataErrors(superBlockInfo_->rawSuper, config_->hmfsErrors);
        return SUCCESS_CODE;
    }

    free(superBlockInfo_->rawSuper);
    superBlockInfo_->rawSuper = nullptr;
    HMFS_ERROR("Can't find a valid F2FS superblock at 0x%x", superBlockAddr);
    return -EINVAL;
}

int32_t LoadSuperBlock::CheckSectorSize(SuperBlockData *superBlock)
{
    HMFS_CHECK_ONLY_EXPER(superBlock != nullptr, return EXIT_ERR_CODE);
    uint32_t logSectorSize = HmfsCommon::GetInstance().LogBase2(config_->sectorSize);
    uint32_t logSectorsPerBlock = HmfsCommon::GetInstance().LogBase2(config_->blockSectorCount);
    if (logSectorSize == GetLeValue(superBlock->logSectorSize) &&
        logSectorsPerBlock == GetLeValue(superBlock->log2SectorsPerBlock)) {
        return SUCCESS_CODE;
    }
    HMFS_INFO("RESIZE version\n  \"%s\"", config_->initVersion);
    SetLeValue(superBlock->logSectorSize, logSectorSize);
    SetLeValue(superBlock->log2SectorsPerBlock, logSectorsPerBlock);

    UpdateSuperBlock(superBlock, SUPER_BLOCK_MASK_ALL);
    return SUCCESS_CODE;
}

int32_t LoadSuperBlock::HmfsLoadSuperBlockData()
{
    HMFS_DEBUG("Enter HmfsLoadSbData");
    if (superBlockInfo_ == nullptr) {
        return EXIT_ERR_CODE;
    }
    int32_t ret = ValidateSuperBlock(SuperBlockAddr::SUPER_BLOCK_0_ADDR);
    if (ret) {
        ret = ValidateSuperBlock(SuperBlockAddr::SUPER_BLOCK_1_ADDR);
        if (ret) {
            return EXIT_ERR_CODE;
        }
    }
    SuperBlockData *superBlockData = superBlockInfo_->rawSuper;
    ret = CheckSectorSize(superBlockData);
    if (ret) {
        return EXIT_ERR_CODE;
    }
    PrintRawSuperBlockInfo(superBlockData, config_->debugLevel, config_->layout);
    return SUCCESS_CODE;
}
} // namespace Hmfs
} // namespace OHOS